﻿using MongoDB.Driver;
using Sustainalytics.Entities.RiskRatings;
using Sustainalytics.ScreeningTool.Synchronization.Caches;
using Sustainalytics.ScreeningTool.Synchronization.Entities;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ScreeningTool.Synchronization.Handlers.RiskRatings
{
    using Performance = RiskRatingRelativePerformance;

    public class BenchmarkHandler : IHandler<RiskRatingBenchmark>
    {
        private readonly MeiNamesCache _meiNamesCache;

        public BenchmarkHandler(MeiNamesCache meiNamesCache)
        {
            _meiNamesCache = meiNamesCache;
        }

        public IEnumerable<ExtraElement> GetExtraElements(RiskRatingBenchmark entity)
            => EnumerableExtensions.Concat(
                GetExtraElements(entity.CompanyBenchmark),
                GetExtraElements(entity.MaterialEsgIssueBenchmarks));

        public ProjectionDefinition<RiskRatingBenchmark> GetProjection(ProjectionDefinitionBuilder<RiskRatingBenchmark> builder) => builder
            .Include(x => x.CompanyBenchmark.UniversePerformanceRisk)
            .Include(x => x.CompanyBenchmark.UniversePerformanceManagement)
            .Include(x => x.CompanyBenchmark.UniversePerformanceExposure)
            .Include(x => x.CompanyBenchmark.IndustryPerformanceRisk)
            .Include(x => x.CompanyBenchmark.IndustryPerformanceManagement)
            .Include(x => x.CompanyBenchmark.IndustryPerformanceExposure)
            .Include(x => x.CompanyBenchmark.SubIndustryPerformanceRisk)
            .Include(x => x.CompanyBenchmark.SubIndustryPerformanceManagement)
            .Include(x => x.CompanyBenchmark.SubIndustryPerformanceExposure)
            .Include(x => x.MaterialEsgIssueBenchmarks);

        public IEnumerable<ExtraElement> GetExtraElements(CompanyBenchmark b)
        {
            var benchmarks = new (Performance performance, string type, string suffix)[]
            {
                ( b.UniversePerformanceRisk,          "ESG Risk",           "Universe"       ),
                ( b.UniversePerformanceManagement,    "Overall Management", "Universe"       ),
                ( b.UniversePerformanceExposure,      "Overall Exposure",   "Universe"       ),
                ( b.IndustryPerformanceRisk,          "ESG Risk",           "Industry Group" ),
                ( b.IndustryPerformanceManagement,    "Overall Management", "Industry Group" ),
                ( b.IndustryPerformanceExposure,      "Overall Exposure",   "Industry Group" ),
                ( b.SubIndustryPerformanceRisk,       "ESG Risk",           "Subindustry"    ),
                ( b.SubIndustryPerformanceManagement, "Overall Management", "Subindustry"    ),
                ( b.SubIndustryPerformanceExposure,   "Overall Exposure",   "Subindustry"    ),
            };

            return benchmarks.SelectMany(x => GetExtraElements(x.performance, x.type, x.suffix));
        }

        public IEnumerable<ExtraElement> GetExtraElements(IEnumerable<MaterialEsgIssuesBenchmark> entities)
            => entities.SelectMany(GetExtraElements);

        public IEnumerable<ExtraElement> GetExtraElements(MaterialEsgIssuesBenchmark b)
        {
            var key = $"Issue - {_meiNamesCache[b.MeiExternalId]}";

            var benchmarks = new (Performance performance, string type, string suffix)[]
            {
                ( b.UniversePerformanceRisk,          $"{key}-Risk",       "Universe"       ),
                ( b.UniversePerformanceManagement,    $"{key}-Management", "Universe"       ),
                ( b.UniversePerformanceExposure,      $"{key}-Exposure",   "Universe"       ),
                ( b.IndustryPerformanceRisk,          $"{key}-Risk",       "Industry Group" ),
                ( b.IndustryPerformanceManagement,    $"{key}-Management", "Industry Group" ),
                ( b.IndustryPerformanceExposure,      $"{key}-Exposure",   "Industry Group" ),
                ( b.SubIndustryPerformanceRisk,       $"{key}-Risk",       "Subindustry"    ),
                ( b.SubIndustryPerformanceManagement, $"{key}-Management", "Subindustry"    ),
                ( b.SubIndustryPerformanceExposure,   $"{key}-Exposure",   "Subindustry"    ),
            };

            return benchmarks.SelectMany(x => GetExtraElements(x.performance, x.type, x.suffix));
        }

        public IEnumerable<ExtraElement> GetExtraElements(Performance performance, string type, string suffix)
            => new ExtraElementList
            {
                { $"{type} Score Average-{suffix}", performance?.Average.Round(2)              },
                { $"{type} Percentile-{suffix}",    performance?.Percentile.PercentileRound(2) },
                { $"{type} Rank-{suffix}",          performance?.GetPeerRanking()              },
            };
    }

    public static class RiskRatingBenchmarkHandlerExtensions
    {
        public static PeerRanking GetPeerRanking(this Performance performance)
            => new PeerRanking(performance.Rank, performance.Count);
    }
}
